package com.candy.codegen.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.candy.codegen.domain.CxSelect;
import com.candy.codegen.domain.entity.GenTable;
import com.candy.codegen.domain.entity.GenTableColumn;
import com.candy.codegen.domain.PageResult;
import com.candy.codegen.manager.GenTableManager;
import com.candy.codegen.mapper.GenTableColumnMapper;
import com.candy.codegen.mapper.GenTableMapper;
import com.candy.codegen.domain.param.GenTableQueryParam;
import com.candy.codegen.domain.param.GenTableSaveParam;
import com.candy.codegen.service.GenTableService;
import com.candy.codegen.util.*;
import com.candy.codegen.domain.vo.GenTableColumnVO;
import com.candy.codegen.domain.vo.GenTableVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.If;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.candy.codegen.domain.entity.table.GenTableColumnTableDef.GEN_TABLE_COLUMN;
import static com.candy.codegen.domain.entity.table.GenTableTableDef.GEN_TABLE;


/**
 * 代码生成表服务类
 * @author rong xi
 * @date 2023/09/14 10:27
 * @version 1.0
 */
@Service
@Slf4j
@AllArgsConstructor
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable>  implements GenTableService {
    private final GenTableMapper genTableMapper;
    private final GenTableColumnMapper genTableColumnMapper;

    /**
     * 根据id查询代码生成表VO
     * @param id 表id
     * @return 表VO对象
     */
    @Override
    public GenTableVO getVoById(Long id) {
        return getByIdOpt(id)
                .map(m->{
                    GenTableVO vo = BeanUtil.copyProperties(m,GenTableVO.class);
                    List<GenTableColumnVO> voList = QueryChain.<GenTableColumn>of(genTableColumnMapper)
                            .select(GEN_TABLE_COLUMN.ALL_COLUMNS)
                            .from(GEN_TABLE_COLUMN)
                            .where(GEN_TABLE_COLUMN.TABLE_ID.eq(vo.getId()))
                            .listAs(GenTableColumnVO.class);
                    // 设置表字段
                    vo.setColumns(voList);
                    // 设置代码生成其他选项值
                    GenTableManager.setTableFromOptions(vo);
                    // 设置主子表信息
                    String subTableName = vo.getSubTableName();
                    if (StringUtils.isNotEmpty(subTableName)) {
                        vo.setSubTable(getVoByTableName(subTableName));
                    }
                    // 设置主键列信息
                    GenTableManager.setPkColumn(vo);
                    return vo;
                })
                .orElse(null);
    }

    /**
     * 根据表名称查询代码生成表VO
     * @param tableName 表名称
     * @return 表VO对象
     */
    @Override
    public GenTableVO getVoByTableName(String tableName) {
        GenTableVO vo = queryChain()
                .select(GEN_TABLE.ALL_COLUMNS)
                .from(GEN_TABLE)
                .where(GEN_TABLE.TABLE_NAME.eq(tableName))
                .oneAs(GenTableVO.class);

        List<GenTableColumnVO> voList = QueryChain.of(genTableColumnMapper)
                .select(GEN_TABLE_COLUMN.ALL_COLUMNS)
                .from(GEN_TABLE_COLUMN)
                .where(GEN_TABLE_COLUMN.TABLE_ID.eq(vo.getId()))
                .listAs(GenTableColumnVO.class);
        // 设置表字段
        vo.setColumns(voList);
        // 设置代码生成其他选项值
        GenTableManager.setTableFromOptions(vo);
        // 设置子表信息
        String subTableName = vo.getSubTableName();
        if (StringUtils.isNotEmpty(subTableName)) {
            vo.setSubTable(getVoByTableName(subTableName));
        }
        // 设置主键列信息
        GenTableManager.setPkColumn(vo);
        return vo;
    }

    /**
     * 分页查询表信息
     * @param param 查询条件对象
     * @return 分页结果
     */
    @Override
    public PageResult<GenTableVO> page(GenTableQueryParam param) {

        Page<GenTableVO> page = queryChain()
                .select(GEN_TABLE.ALL_COLUMNS)
                .from(GEN_TABLE)
                .where(GEN_TABLE.TABLE_NAME.like(param.getTableName(), If::hasText))
                .and(GEN_TABLE.TABLE_COMMENT.like(param.getTableComment(), If::hasText))
                .and(GEN_TABLE.CREATE_TIME.ge(param.getStartTime(),If::notNull))
                .and(GEN_TABLE.CREATE_TIME.le(param.getEndTime(),If::notNull))
                .orderBy(param.getOrderBy(GEN_TABLE))
                .page(param.getPage())
                .map(t -> BeanUtil.copyProperties(t, GenTableVO.class));
        return PageResult.of(page);
    }


    /**
     * 分页查询数据库表信息
     * @param param 查询参数
     * @return 分页结果
     */
    @Override
    public PageResult<GenTableVO> pageDb(GenTableQueryParam param) {

        QueryWrapper query = new QueryWrapper();
        query.select(GEN_TABLE.TABLE_NAME,GEN_TABLE.TABLE_COMMENT,GEN_TABLE.CREATE_TIME,GEN_TABLE.UPDATE_TIME,GEN_TABLE.TABLE_SCHEMA);
        query.from(" information_schema.TABLES` as `gen_table");
        query.where("table_schema = (SELECT DATABASE())");
        query.and(GEN_TABLE.TABLE_NAME.notLikeLeft("QRTZ_"));
        query.and(GEN_TABLE.TABLE_NAME.notLikeLeft("gen_"));
        query.and(("table_name not in (SELECT table_name FROM gen_table)"));
        if(StrUtil.isNotBlank(param.getTableName())){
            query.and(GEN_TABLE.TABLE_NAME.like(param.getTableName()));
        }
        if(StrUtil.isNotBlank(param.getTableComment())){
            query.and(GEN_TABLE.TABLE_COMMENT.like(param.getTableComment()));
        }
        query.orderBy(param.getOrderBy(GEN_TABLE));
        Page<GenTableVO> page = this.pageAs(param.getPage(), query, GenTableVO.class);
        return PageResult.of(page);
    }


    /**
     * 查询所有的代码生成表列表
     * @return 表VO列表
     */
    @Override
    public List<GenTableVO> listAllVo() {
        List<GenTableVO> voList = listAs(QueryWrapper.create(),GenTableVO.class);
        Map<Long, List<GenTableColumnVO>> collectMap =
                genTableColumnMapper.selectListByQueryAs(QueryWrapper.create(),GenTableColumnVO.class)
                .stream().collect(Collectors.groupingBy(GenTableColumnVO::getTableId));
        for (GenTableVO vo : voList) {
            vo.setColumns(collectMap.get(vo.getId()));
        }
        return voList;
    }

    /**
     * 更新代码生成表
     * @param param 代码生成表名称
     */
    @Override
    @Transactional
    public void modify(GenTableSaveParam param) {
        //校验参数
        GenTableManager.validateEdit(param);
        param.setOptions(JSON.toJSONString(param.getParams()));
        GenTable genTable = BeanUtil.copyProperties(param, GenTable.class);
        genTable.setUpdateTime(LocalDateTime.now());
        genTableMapper.update(genTable);
        List<GenTableColumn> columns = BeanUtil.copyToList(param.getColumns(), GenTableColumn.class);
        for (GenTableColumn column : columns) {
            if(column.getIsList() == null){
                column.setIsList(0);
            }
            if(column.getIsQuery() == null){
                column.setIsQuery(0);
            }
            if(column.getIsEdit() == null){
                column.setIsEdit(0);
            }
            if(column.getIsInsert() == null){
                column.setIsInsert(0);
            }
            if(column.getIsVerify() == null){
                column.setIsVerify(0);
            }
            if(column.getIsRequired() == null){
                column.setIsRequired(0);
            }
            column.setUpdateTime(LocalDateTime.now());
            genTableColumnMapper.update(column);
        }
    }

    /**
     * 根据ids删除代码生成表
     * @param ids 代码生成表ids
     */
    @Override
    @Transactional
    public void delete(String ids) {
        List<String> tableIdList = StrUtil.split(ids, ",");
        genTableMapper.deleteBatchByIds(tableIdList);
        genTableColumnMapper.deleteByCondition(GEN_TABLE_COLUMN.TABLE_ID.in(tableIdList));
    }


    /**
     * 根据id生成预览代码
     * @param id 表id
     * @return 代码map
     */
    @Override
    public Map<String, String> previewCode(Long id) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        // 查询表信息
        GenTableVO tableVO = getVoById(id);
        //初始化vm
        VelocityInitializer.initVelocity();
        //设置变量内容
        VelocityContext context = VelocityUtils.prepareContext(tableVO);
        // 获取模板路径列表
        List<String> templates = VelocityUtils.getTemplateList(tableVO.getTplCategory());
        for (String template : templates) {
            StringWriter sw = new StringWriter();
            //获取此模板
            Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
            // 渲染模板
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return dataMap;
    }

    /**
     * 生成代码（自定义路径）
     * @param id 表id
     */
    @Override
    public void generatorCode(Long id) {

        GenTableVO tableVO = getVoById(id);
        VelocityInitializer.initVelocity();

        VelocityContext context = VelocityUtils.prepareContext(tableVO);

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(tableVO.getTplCategory());
        for (String template : templates) {
            if (!StringUtils.contains(template, "menu.sql.vm")) {
                // 渲染模板
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
                tpl.merge(context, sw);
                    String path = GenTableManager.getGenPath(tableVO, template);
                    try {
                        FileUtil.writeString(sw.toString(), new File(path), CharsetUtil.UTF_8);
                    }catch (Exception e){
                        throw new RuntimeException("渲染模板失败，表名：" + tableVO.getTableName());
                    }
            }
        }
    }

    /**
     * 批量生成代码
     * @param ids 表ids
     * @return 代码字节数组
     */
    @Override
    public byte[] batchDownloadCode(String ids) {
        List<Long> tableIdsList = StrUtil.split(ids, ",").stream().map(Long::valueOf).toList();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (Long tableId : tableIdsList) {
            generatorCode(tableId, zip);
        }
        IoUtil.closeIfPosible(zip);
        return outputStream.toByteArray();
    }

    /**
     * 根据id自定义路径生成代码
     * @param id 表id
     */
    private void generatorCode(Long id, ZipOutputStream zip) {
        GenTableVO tableVO = getVoById(id);

        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(tableVO);

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(tableVO.getTplCategory());
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
            tpl.merge(context, sw);
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, tableVO)));

                IoUtil.write(zip, StandardCharsets.UTF_8,false,sw.toString());
                IoUtil.closeIfPosible(sw);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + tableVO.getTableName(), e);
            }
        }
    }


    /**
     * 查询表信息并存储
     * @param tableNames 表名称
     */
    @Override
    @Transactional
    public void importGenTable(String tableNames) {
        List<String> tableIds = StrUtil.split(tableNames, ",");
        importGenTable(GenTableManager.listDBTableByTableNames(tableIds));
    }

    /**
     * 根据表id查询表更新信息
     * @param id 表id
     * @param modelMap 视图map对象
     */
    @Override
    public void getEditInfo(Long id, ModelMap modelMap) {
        GenTableVO tableVO = getVoById(id);

        List<GenTableVO> voList = listAllVo();
        List<CxSelect> cxSelect = new ArrayList<>();
        for (GenTableVO vo : voList) {
            if (!StringUtils.equals(tableVO.getTableName(), vo.getTableName())) {
                CxSelect cxTable = new CxSelect(vo.getTableName(), vo.getTableName() + '：' + vo.getTableComment(),null);
                List<CxSelect> cxColumns = new ArrayList<>();
                for (GenTableColumnVO tableColumn : vo.getColumns()) {
                    cxColumns.add(new CxSelect(tableColumn.getColumnName(), tableColumn.getColumnName() + '：' + tableColumn.getColumnComment(),null));
                }
                cxTable.setS(cxColumns);
                cxSelect.add(cxTable);
            }
        }
        modelMap.put("table", tableVO);
        modelMap.put("data", JSON.toJSON(cxSelect));
    }


    /**
     * 导入代码生成表
     * @param tableList 生成表列表
     */
    @Override
    @Transactional
    public void importGenTable(@NonNull List<GenTable> tableList) {
        //保存初始化好的字段信息
        List<GenTableColumn> genTableColumns = new ArrayList<>();

        Map<String,List<GenTableColumn>> columnListMap = genTableColumnMapper.selectDbTableColumnsByNames(
                        tableList.stream().map(GenTable::getTableName).toList())
                .stream().collect(Collectors.groupingBy(GenTableColumn::getTableName));

        for (GenTable table : tableList) {
            //初始化表其他信息
            GenUtils.initTableField(table);
            for (GenTableColumn column : columnListMap.get(table.getTableName())) {
                //初始化字段信息
                GenUtils.initColumnField(column, table);
                genTableColumns.add(column);
            }
        }
        //保存表信息
        genTableMapper.insertBatch(tableList);
        // 保存列信息
        genTableColumnMapper.insertBatch(genTableColumns);
    }

}